/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 get_artifact_damage_modifier.cpp

	$Header: /heroes4/get_artifact_damage_modifier.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "get_artifact_damage_modifier.h"

#include "artifact_effect_visitor.h"
#include "artifact_prop_ability.h"
#include "artifact_prop_combat.h"
#include "artifact_prop_damage.h"
#include "artifact_prop_income.h"
#include "artifact_prop_movement.h"
#include "artifact_prop_single_spell.h"
#include "artifact_prop_skill.h"
#include "artifact_prop_spell_cost.h"
#include "external_string.h"
#include "game_replace_keywords.h"
#include "hero.h"
#include "skill.h"
#include "spell_properties.h"

namespace
{
	// ---------------------------------------------
	// visitor to find weapon modifiers
	// ---------------------------------------------
	class t_artifact_weapon_visitor : public t_artifact_effect_visitor
	{
	public:
		t_artifact_weapon_visitor( bool ranged );

		int			 get_modifier() const;
		virtual bool visit_combat( t_artifact_prop::t_combat& effect );
	protected:
		int  m_modifier;
		bool m_ranged;
	};

	inline int t_artifact_weapon_visitor::get_modifier() const
	{
		return m_modifier;
	}

	// ---------------------------------------------
	// visitor to find armor value
	// ---------------------------------------------
	class t_artifact_armor_visitor : public t_artifact_weapon_visitor
	{
	public:
		t_artifact_armor_visitor( bool ranged, t_artifact_target target );

		virtual bool visit_combat( t_artifact_prop::t_combat& effect );
	protected:
		t_artifact_target m_target;
	};

};

// ---------------------------------------------
// visitor to find weapon modifiers
// ---------------------------------------------
t_artifact_weapon_visitor::t_artifact_weapon_visitor( bool ranged )
{
	m_ranged = ranged;
	m_modifier = 0;
}

// ---------------------------------------------
// visitor to find weapon modifiers
// ---------------------------------------------
bool t_artifact_weapon_visitor::visit_combat( t_artifact_prop::t_combat& effect )
{
	if (effect.get_effect() != k_artifact_effect_weapon)
		return false;
	if (!effect.affects( m_ranged ))
		return false;
	m_modifier += effect.get_amount();
	return false;
}

// get total effect of all weapons on a hero's damage, as a percentage.
int get_artifact_damage_modifier( t_hero const& hero, bool ranged )
{
	t_artifact_weapon_visitor visitor( ranged );

	hero.accept( visitor );
	return visitor.get_modifier();
}

// ---------------------------------------------
// visitor to find armor value
// ---------------------------------------------
t_artifact_armor_visitor::t_artifact_armor_visitor( bool ranged, t_artifact_target target ) 
                        : t_artifact_weapon_visitor( ranged )
{
	m_target = target;
}


// ---------------------------------------------
// visitor to find armor value
// ---------------------------------------------
bool t_artifact_armor_visitor::visit_combat( t_artifact_prop::t_combat& effect )
{
	if (effect.get_effect() != k_artifact_effect_defense)
		return false;
	if (effect.get_target() != m_target)
		return false;
	if (!effect.affects( m_ranged ))
		return false;
	m_modifier += effect.get_amount();
	return false;
}

// get total effect of all armor, as a percentage
int get_artifact_armor_value( t_hero const& hero, bool ranged )
{
	t_artifact_armor_visitor visitor( ranged, k_artifact_target_hero );

	hero.accept( visitor );
	return visitor.get_modifier();
}

// get amount added to creature defense
int get_artifact_creature_defense_bonus( t_hero const& hero, bool ranged )
{
	t_artifact_armor_visitor visitor( ranged, k_artifact_target_friendly_creatures );

	hero.accept( visitor );
	return visitor.get_modifier();
}

namespace
{
	// --------------------------------------------------------------
	// visitor for spell cost effects
	// --------------------------------------------------------------
	class t_spell_cost_visitor : public t_artifact_effect_visitor
	{
	public:
		t_spell_cost_visitor( t_spell spell );

		int			 get_modifier() const;
		int			 get_percentage() const;
		virtual bool visit_spell_cost( t_artifact_prop::t_spell_cost_base& effect );
	protected:
		int		m_modifier;
		int		m_percentage;
		t_spell m_spell;
	};

	inline int t_spell_cost_visitor::get_modifier() const
	{
		return m_modifier;
	}

	inline int t_spell_cost_visitor::get_percentage() const
	{
		return m_percentage;
	}

};

// --------------------------------------------------------------
// visitor for spell cost effects
// --------------------------------------------------------------
t_spell_cost_visitor::t_spell_cost_visitor( t_spell spell )
{
	m_spell = spell;
	m_modifier = 0;
	m_percentage = 100;
}

// --------------------------------------------------------------
// visitor for spell cost effects
// --------------------------------------------------------------
bool t_spell_cost_visitor::visit_spell_cost( t_artifact_prop::t_spell_cost_base& effect )
{
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;
	if (!effect.has_spell( m_spell ))
		return false;

	m_modifier += effect.get_amount();
	m_percentage = m_percentage * (100 + effect.get_percentage()) / 100;
	return false;
}

// get modifier of spell costs based on artifacts
int get_artifact_spell_cost( t_hero const& hero, t_spell spell )
{
	t_spell_cost_visitor visitor( spell );
	int					 result = get_spell_cost( spell );

	hero.accept( visitor );
	result = result * visitor.get_percentage() / 100;
	result += visitor.get_modifier();
	if (result < 1)
		result = 1;
	return result;
}

namespace
{
	// --------------------------------------------------------------
	// visitor for spell cost effects
	// --------------------------------------------------------------
	class t_spell_power_visitor : public t_artifact_effect_visitor
	{
	public:
		t_spell_power_visitor( t_spell spell );

		int			 get_modifier() const;
		virtual bool visit_spell_list( t_artifact_prop::t_spell_list_effect& effect );
	protected:
		int		m_modifier;
		t_spell m_spell;
	};

	inline int t_spell_power_visitor::get_modifier() const
	{
		return m_modifier;
	}

};

// --------------------------------------------------------------
// visitor for spell cost effects
// --------------------------------------------------------------
t_spell_power_visitor::t_spell_power_visitor( t_spell spell )
{
	m_spell = spell;
	m_modifier = 0;
}

// --------------------------------------------------------------
// visitor for spell cost effects
// --------------------------------------------------------------
bool t_spell_power_visitor::visit_spell_list( t_artifact_prop::t_spell_list_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_spell_bonus)
		return false;
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;
	if (!effect.has_spell( m_spell ))
		return false;

	m_modifier += effect.get_amount();
	return false;
}


// get modifier of spell power based on artifacts, which is a percentage
int get_artifact_spell_power_modifier( t_hero const& hero, t_spell spell )
{
	t_spell_power_visitor visitor( spell );

	hero.accept( visitor );
	return visitor.get_modifier();
}

namespace
{
	// --------------------------------------------------------------
	// get percentage modifier for a skill
	// --------------------------------------------------------------
	class t_skill_power_visitor : public t_artifact_effect_visitor
	{
	public:
		t_skill_power_visitor( t_skill_type skill );

		int			 get_modifier() const;
		virtual bool visit_skill( t_artifact_prop::t_skill_effect& effect );
	protected:
		int			 m_modifier;
		t_skill_type m_skill;
	};

	inline int t_skill_power_visitor::get_modifier() const
	{
		return m_modifier;
	}
};

// --------------------------------------------------------------
// get percentage modifier for a skill
// --------------------------------------------------------------
t_skill_power_visitor::t_skill_power_visitor( t_skill_type skill )
{
	m_skill = skill;
	m_modifier = 0;
}

// --------------------------------------------------------------
// get percentage modifier for a skill
// --------------------------------------------------------------
bool t_skill_power_visitor::visit_skill( t_artifact_prop::t_skill_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_skill)
		return false;
	if (effect.get_skill() != m_skill)
		return false;
	m_modifier += effect.get_amount();
	return false;
}

// --------------------------------------------------------------
// get percentage modifier for a skill
// --------------------------------------------------------------
int get_artifact_skill_modifier( t_hero const& hero, t_skill_type skill )
{
	t_skill_power_visitor visitor( skill );

	hero.accept( visitor );
	return visitor.get_modifier();
}

namespace
{
	// --------------------------------------------------------------
	// visitor to sum values of a type of artifact effect
	// --------------------------------------------------------------
	class t_sum_visitor : public t_artifact_effect_visitor
	{
	public:
		t_sum_visitor( t_artifact_effect_type type, t_artifact_target target );

		int			 get_modifier() const;
		virtual bool visit( t_artifact_effect& effect );
	protected:
		int					   m_modifier;
		t_artifact_target		m_target;
		t_artifact_effect_type m_type;
	};

	inline t_sum_visitor::t_sum_visitor( t_artifact_effect_type type, t_artifact_target target )
	{
		m_type = type;
		m_modifier = 0;
		m_target = target;
	}

	inline int t_sum_visitor::get_modifier() const
	{
		return m_modifier;
	}
};

// --------------------------------------------------------------
// get speed modifier
// --------------------------------------------------------------
bool t_sum_visitor::visit( t_artifact_effect& effect )
{
	if (effect.get_effect() != m_type)
		return false;
	if (effect.get_target() != m_target
		&& effect.get_target() != k_artifact_target_everything)
		return false;
	m_modifier += effect.get_amount();
	return false;
}

// --------------------------------------------------------------
// get speed modifier
// --------------------------------------------------------------
static int get_artifact_modifier( t_hero const& hero, t_artifact_effect_type type,
								  t_artifact_target target = k_artifact_target_hero)
{
	t_sum_visitor visitor( type, target );

	hero.accept( visitor );
	return visitor.get_modifier();
}

// --------------------------------------------------------------
// get speed modifier
// --------------------------------------------------------------
int get_artifact_speed_modifier( t_hero const& hero, t_artifact_target target )
{
	return get_artifact_modifier( hero, k_artifact_effect_speed, target );
}

// get amount added to creature attacks
int get_artifact_creature_attack_bonus( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_damage, 
								  k_artifact_target_friendly_creatures );
}

int get_artifact_scouting_bonus( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_scouting );
}

namespace
{
	// --------------------------------------------------------------
	// visitor sum increases to income
	// --------------------------------------------------------------
	class t_income_visitor : public t_artifact_effect_visitor
	{
	public:
		t_income_visitor( t_material_array& result_array );

		virtual bool visit_income( t_artifact_prop::t_income& effect );
	protected:
		t_material_array& m_result_array;
	};

	inline t_income_visitor::t_income_visitor( t_material_array& result_array  )
						   : m_result_array( result_array )
	{
	}
};

// --------------------------------------------------------------
// visit the income effect and sum it
// --------------------------------------------------------------
bool t_income_visitor::visit_income( t_artifact_prop::t_income& effect )
{
	m_result_array += effect.get_bonus();
	return false;
}

// --------------------------------------------------------------
// get total income from all artifacts equipped by the hero
// --------------------------------------------------------------
void get_artifact_income( t_hero const& hero, t_material_array& result )
{
	t_income_visitor visitor( result );

	result.set( 0 );
	hero.accept( visitor );
}


// --------------------------------------------------------------
// get extra damage done by weapons, by type
// --------------------------------------------------------------
namespace
{
	class t_magic_weapon_visitor : public t_artifact_effect_visitor
	{
	public:
		t_magic_weapon_visitor( int level, bool ranged );

		int			 get_damage( std::vector<int>& damage );
		virtual bool visit_damage( t_artifact_prop::t_damage_bonus_base& effect );
	protected:
		int  m_damage[k_damage_type_count];
		int  m_level;
		bool m_ranged;
	};
};

// --------------------------------------------------------------
// get extra damage done by weapons, by type
// --------------------------------------------------------------
t_magic_weapon_visitor::t_magic_weapon_visitor( int level, bool ranged )
{
	m_level = level;
	m_ranged = ranged;
	memset( m_damage, 0, sizeof( m_damage ));
}

// --------------------------------------------------------------
// get extra damage done by weapons, by type
// --------------------------------------------------------------
int t_magic_weapon_visitor::get_damage( std::vector<int>& damage )
{
	int i;
	int total = 0;

	damage.resize( k_damage_type_count );
	for (i = 0; i < k_damage_type_count; i++)
	{
		damage[i] = m_damage[i];
		total += m_damage[i];
	}
	return total;
}

// --------------------------------------------------------------
// get extra damage done by weapons, by type
// --------------------------------------------------------------
bool t_magic_weapon_visitor::visit_damage( t_artifact_prop::t_damage_bonus_base& effect )
{
	int damage;

	if (effect.get_effect() != k_artifact_effect_damage_bonus)
		return false;
	if (effect.get_target() != k_artifact_target_hero)
		return false;
	if (!effect.affects( m_ranged ))
		return false;

	damage = effect.get_amount() 
		+ effect.get_additional_bonus() * m_level / effect.get_levels_per_bonus();
	m_damage[effect.get_damage_type()] += damage;
	return false;
}

// --------------------------------------------------------------
// get extra damage done by weapons, by type
// --------------------------------------------------------------
int get_artifact_damage( t_hero const& hero, bool ranged, std::vector<int>& damage )
{
	t_magic_weapon_visitor visitor( hero.get_level(), ranged );
	int					   total = 0;

	hero.accept( visitor );
	return visitor.get_damage( damage );
}

// --------------------------------------------------------------
// get extra damage done by weapons, by type
// --------------------------------------------------------------
int get_artifact_damage( t_creature_stack const& creature, bool ranged )
{
	t_hero const*    hero = creature.get_hero();
	std::vector<int> damage;

	if (hero == 0)
		return 0;
	return get_artifact_damage( *hero, ranged, damage );
}

	
// --------------------------------------------------------------
// check if hero has equipped a terrain mastery artifact
// --------------------------------------------------------------
namespace
{
	class t_terrain_visitor : public t_artifact_effect_visitor
	{
	public:
		t_terrain_visitor( t_terrain_type terrain );

		virtual bool visit( t_artifact_effect& effect );
	protected:
		t_terrain_type m_terrain;
	};

};

t_terrain_visitor::t_terrain_visitor( t_terrain_type terrain )
{
	m_terrain = terrain;
}

bool t_terrain_visitor::visit( t_artifact_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_terrain_mastery)
		return false;
	return (effect.get_amount() == m_terrain);
}

bool has_terrain_artifact( t_hero const& hero, t_terrain_type terrain )
{
	t_terrain_visitor visitor( terrain );

	return hero.accept( visitor );
}

// --------------------------------------------------------------
// get any movement bonuses for artifacts
// --------------------------------------------------------------
namespace
{
	class t_movement_visitor : public t_artifact_effect_visitor
	{
	public:
		t_movement_visitor( bool sea_move );

		int			 get_modifier() const;
		virtual bool visit_movement( t_artifact_prop::t_movement& effect );
	protected:
		int  m_modifier;
		bool m_sea_move;
	};
};

// --------------------------------------------------------------
// get any movement bonuses for artifacts
// --------------------------------------------------------------
t_movement_visitor::t_movement_visitor( bool sea_move )
{
	m_modifier = 0;
	m_sea_move = sea_move;
}

inline int t_movement_visitor::get_modifier() const
{
	return m_modifier;
}

bool t_movement_visitor::visit_movement( t_artifact_prop::t_movement& effect )
{
	if (m_sea_move)
	{
		if (!effect.affects_sea())
			return false;
	}
	else
	{
		if (!effect.affects_land())
			return false;
	}
	m_modifier += effect.get_amount();
	return false;
}

// get adventure map move bonus due to artifacts
int get_artifact_adventure_move_bonus( t_hero const& hero, bool sea_move )
{
	t_movement_visitor visitor( sea_move );

	hero.accept( visitor );
	return visitor.get_modifier();
}

// --------------------------------------------------------------
// get increase in spell points due to artifacts
// --------------------------------------------------------------
int get_artifact_spell_point_modifier( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_mana_maximum );
}

// --------------------------------------------------------------
// get increase in recharge rate due to artifacts
// --------------------------------------------------------------
int get_artifact_spell_point_recovery( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_mana_recharge );
}

// --------------------------------------------------------------
// get increase in luck due to artifacts
// --------------------------------------------------------------
int get_artifact_luck( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_luck );
}

// --------------------------------------------------------------
// get increase in morale due to artifacts
// --------------------------------------------------------------
int get_artifact_morale( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_morale );
}

// --------------------------------------------------------------
// get artifact magic resistance modifier
// --------------------------------------------------------------
namespace
{
	class t_resistance_visitor : public t_artifact_effect_visitor
	{
	public:
		t_resistance_visitor();

		int				get_modifier() const;
		virtual bool	visit( t_artifact_effect& effect );

	private:
		int m_modifier;
	};
}

t_resistance_visitor::t_resistance_visitor()
	:	m_modifier( 0 )
{
}

int t_resistance_visitor::get_modifier() const
{
	return m_modifier;
}

bool t_resistance_visitor::visit( t_artifact_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_magic_resistance)
		return false;
	if (effect.get_target() != k_artifact_target_hero
		&& effect.get_target() != k_artifact_target_everything)
		return false;
	m_modifier += ( 100 - m_modifier ) * effect.get_amount() / 100;
	return false;
}

// --------------------------------------------------------------
// get artifact magic resistance modifier
// --------------------------------------------------------------
int get_artifact_magic_resistance( t_hero const& hero )
{
	t_resistance_visitor visitor;

	hero.accept( visitor );
	return visitor.get_modifier();
}

// --------------------------------------------------------------
// check if hero has a scroll
// --------------------------------------------------------------
namespace
{
	class t_scroll_visitor : public t_artifact_effect_visitor
	{
	public:
		t_scroll_visitor( t_hero& hero );

		virtual bool visit_spell( t_artifact_prop::t_single_spell& effect );
		virtual bool visit_spell_list( t_artifact_prop::t_spell_list_effect& effect );
	protected:
		t_hero& m_hero;
	};
};

// --------------------------------------------------------------
// check if hero has a scroll
// --------------------------------------------------------------
inline t_scroll_visitor::t_scroll_visitor( t_hero& hero )
					   : m_hero( hero )
{
}

bool t_scroll_visitor::visit_spell( t_artifact_prop::t_single_spell& effect )
{
	if (effect.get_effect() != k_artifact_effect_cast_spell)
		return false;
	m_hero.add_scroll_spell( effect.get_spell() );
	return false;
}

bool t_scroll_visitor::visit_spell_list( t_artifact_prop::t_spell_list_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_spell_book)
		return false;
	
	std::set<t_spell> const&			spells = effect.get_spells();
	std::set<t_spell>::const_iterator index = spells.begin();
	std::set<t_spell>::const_iterator end = spells.end();

	for (; index != end; ++index)
		m_hero.add_book_spell( *index );
	return false;
}


void set_artifact_spells( t_hero& hero )
{
	t_scroll_visitor visitor( hero );

	hero.accept( visitor );
}


// --------------------------------------------------------------
// attempt to use a parchment
// --------------------------------------------------------------
namespace
{
	class t_parchment_visitor : public t_artifact_effect_visitor
	{
	public:
		t_parchment_visitor( t_hero& hero );

		std::string  get_message() const;
		bool		 used() const;
		virtual bool visit_spell( t_artifact_prop::t_single_spell& effect );

	protected:
		t_hero&		m_hero;
		std::string m_message;
		bool		m_result;
	};

	inline t_parchment_visitor::t_parchment_visitor( t_hero& hero )
							  : m_hero( hero )
	{
		m_result = false;
	}

	inline std::string t_parchment_visitor::get_message() const
	{
		return m_message;
	}

	inline bool t_parchment_visitor::used() const
	{
		return m_result;
	}
};

// --------------------------------------------------------------
// attempt to use a parchment
// --------------------------------------------------------------
static t_external_string const k_text_already_known( "damage_modifier_already_known.misc" );
static t_external_string const k_text_insufficient_skill( "damage_modifier_insufficient_skill.misc" );
static t_external_string const k_text_learned( "damage_modifier_learned.misc" );

bool t_parchment_visitor::visit_spell( t_artifact_prop::t_single_spell& effect )
{
	if (effect.get_effect() != k_artifact_effect_learn_spell)
		return false;

	t_spell spell = effect.get_spell();

	if (!m_message.empty())
		m_message += "\n";
	if (m_hero.in_spellbook( spell ))
	{
		m_message += k_text_already_known;
	}
	else if (!m_hero.learn_spell( spell ))
	{
		t_town_type alignment = get_spell_alignment( spell );
		int			level = get_spell_level( spell );
		t_skill     skill;

		skill.skill = t_skill_type( alignment + k_skill_life_magic );
		skill.level = t_skill_mastery( level + k_mastery_none );
		m_message += k_text_insufficient_skill;
		m_message = replace_keywords( m_message, skill );
	}
	else
	{
		m_message += k_text_learned;
		m_result = true;
	}

	m_message = replace_keywords( m_message, &m_hero );
	m_message = replace_keywords( m_message, "%spell_name", get_spell_name( spell ));
	return false;
}

// --------------------------------------------------------------
// attempt to use a parchment
// --------------------------------------------------------------
bool use_parchment( t_hero& hero, t_artifact const& artifact, std::string* message )
{
	t_parchment_visitor visitor( hero );

	artifact.accept( visitor );
	if (message != 0)
		*message = visitor.get_message();
	return visitor.used();
}

// --------------------------------------------------------------
// get discount due to penny-pinching
// --------------------------------------------------------------
namespace
{
	class t_discount_visitor : public t_artifact_effect_visitor
	{
	public:
		t_discount_visitor();

		int			 get_result() const;
		virtual bool visit( t_artifact_effect& effect );
	protected:
		int m_result;
	};

	inline t_discount_visitor::t_discount_visitor()
	{
		m_result = 0;
	}

	inline int t_discount_visitor::get_result() const
	{
		return m_result;
	}
};


// --------------------------------------------------------------
// get discount due to penny-pinching
// --------------------------------------------------------------
bool t_discount_visitor::visit( t_artifact_effect& effect )
{
	if (effect.get_effect() != k_artifact_effect_creature_discount)
		return false;
	if (m_result < effect.get_amount())
		m_result = effect.get_amount();
	return false;
}

// --------------------------------------------------------------
// get discount due to penny-pinching
// --------------------------------------------------------------
int get_artifact_recruitment_discount( t_hero const& hero )
{
	t_discount_visitor visitor;

	hero.accept( visitor );
	return visitor.get_result();
}

// --------------------------------------------------------------
// get health bonus
// --------------------------------------------------------------
int get_artifact_health_bonus( t_hero const& hero )
{
	return get_artifact_modifier( hero, k_artifact_effect_health );
}

// --------------------------------------------------------------
// check if artifact effect exists
// --------------------------------------------------------------
namespace
{
	class t_effect_presence_visitor : public t_artifact_effect_visitor
	{
	public:
		explicit t_effect_presence_visitor( t_artifact_effect_type type );

		virtual bool visit( t_artifact_effect& effect );
	protected:
		t_artifact_effect_type m_type;
	};

	inline t_effect_presence_visitor::t_effect_presence_visitor( t_artifact_effect_type type )
	{
		m_type = type;
	}

};

bool t_effect_presence_visitor::visit( t_artifact_effect& effect )
{
	return effect.get_effect() == m_type;
}

static bool has_effect( t_hero const& hero, t_artifact_effect_type type, t_artifact** artifact )
{
	t_effect_presence_visitor	visitor( type );
	bool						result;

	result = hero.accept( visitor );
	if (result && artifact != 0)
		*artifact = (t_artifact*)visitor.get_artifact();
	return result;
}

bool has_seamans_hat( t_hero const& hero )
{
	return has_effect( hero, k_artifact_effect_seamanship, 0 );
}

bool has_shackles_of_war( t_hero const& hero, t_artifact*& artifact )
{
	return has_effect( hero, k_artifact_effect_prohibits_retreat, &artifact );
}

// --------------------------------------------------------------
// check if hero has an artifact that gives an ability
// --------------------------------------------------------------
namespace
{
	class t_ability_presence_visitor : public t_artifact_effect_visitor
	{
	public:
		explicit t_ability_presence_visitor( t_creature_ability type );

		virtual bool visit_ability( t_artifact_prop::t_give_ability& effect );
	protected:
		t_creature_ability m_type;
	};

	inline t_ability_presence_visitor::t_ability_presence_visitor( t_creature_ability type )
	{
		m_type = type;
	}

};

bool t_ability_presence_visitor::visit_ability( t_artifact_prop::t_give_ability& effect )
{
	if (effect.get_target() != k_artifact_target_hero)
		return false;
	return effect.get_ability() == m_type;
}


// check if hero has any artifact that gives an ability
bool artifact_gives_ability( t_hero const& hero, t_creature_ability ability )
{
	t_ability_presence_visitor visitor( ability );

	return hero.accept( visitor );
}
